﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SnapLib;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using LibBase;

namespace KeyboardRemapper
{
   public class Macro
   {
      public enum MacroActionType { KeyStroke, WriteString, Sleep, Hibernate, Run, AppMenu, Remember, GoTo, Restart };
      private Keys m_SimulateKey = Keys.None;
      private string m_chars = null;
      private string m_Run = null;

      public MacroActionType pMacroAction { get; set; }
      public List<int> pModifiers { get; set; }
      public List<int> pNotModifiers { get; set; }
      public int pKey { get; set; }
      public int pDevice { get; set; }
      public frmAppMenu.AppList pAppList;
      public int pSlot { get; set; }
      static ForeignWindow[] m_windows = new ForeignWindow[10];

      public string pChars
      {
         get { return m_chars; }
         set { m_chars = value; }
      }

      public Keys pSimulateKey
      {
         get { return m_SimulateKey; }
         set { m_SimulateKey = value; }
      }

      public string pRun
      {
         get { return m_Run; }
         set { m_Run = value; }
      }

      public Macro(string line)
      {
         line = line.Trim();
         if (line.StartsWith("["))
         {
            pDevice = line.AfterString("[").BeforeString("]").ToInt(-1);
            line = line.AfterString("]").TrimStart();
         }
         else
            pDevice = -1;

         string order = line.AfterString(",");
         line = line.BeforeString(",");
         pModifiers = new List<int>();
         pNotModifiers = new List<int>();
         if (line.Contains("+"))
         {
            while (line.Contains("+"))
            {
               if (line.StartsWith("!"))
               {
                  int iModifier = line.Substring(1).BeforeString("+").ToInt(-1);
                  if (iModifier != -1)
                     pNotModifiers.Add(iModifier);
               }
               else
               {
                  int iModifier = line.BeforeString("+").ToInt(-1);
                  if (iModifier != -1)
                     pModifiers.Add(iModifier);
               }

               line = line.AfterString("+");
            }
         }
         pKey = line.ToInt(-1);

         if (order.ToLower().StartsWith("key "))
         {
            pMacroAction = MacroActionType.KeyStroke;
            m_SimulateKey = (Keys)order.AfterString(" ").ToInt(0);
            if (m_SimulateKey == Keys.None)
            {
               string name = order.AfterString(" ").ToLower().Trim();
               foreach (Keys k in Enum.GetValues(typeof(Keys)))
               {
                  if (k.ToString().ToLower() == name)
                     m_SimulateKey = k;
               }
            }
         }
         else if (order.ToLower().StartsWith("run "))
         {
            pMacroAction = MacroActionType.Run;
            m_Run = order.AfterString(" ");
         }
         else if (order.ToLower().StartsWith("string "))
         {
            pMacroAction = MacroActionType.WriteString;
            m_chars = order.AfterString(" ");
         }
         else if (order.Trim().ToLower() == "sleep")
         {
            pMacroAction = MacroActionType.Sleep;
         }
         else if (order.Trim().ToLower() == "hibernate")
         {
            pMacroAction = MacroActionType.Hibernate;
         }
         else if (order.ToLower().Trim() == "appmenu")
         {
            pMacroAction = MacroActionType.AppMenu;
         }
         else if (order.ToLower().StartsWith("getw "))
         {
            pMacroAction = MacroActionType.Remember;
            pSlot = order.AfterString(" ").ToInt(1);
         }
         else if (order.ToLower() == "restart")
         {
            pMacroAction = MacroActionType.Restart;
         }
         else if (order.ToLower().StartsWith("setw "))
         {
            pMacroAction = MacroActionType.GoTo;
            pSlot = order.AfterString(" ").ToInt(1);
         }
      }

      public override string ToString()
      {
         string res = "";
         switch (pMacroAction)
         {
            case MacroActionType.KeyStroke:
               res += "key " + pSimulateKey.ToString();
               break;
            case MacroActionType.Hibernate:
               res += "hibernate";
               break;
            case MacroActionType.Sleep:
               res += "sleep";
               break;
            case MacroActionType.WriteString:
               res += "string " + pChars;
               break;
            case MacroActionType.Run:
               res += "run " + pRun;
               break;
            case MacroActionType.AppMenu:
               res += "AppMenu (";
               foreach (frmAppMenu.AppInfo appInfo in pAppList)
               {
                  if (appInfo != pAppList[0])
                     res += ", ";
                  res += appInfo.ToString();
               }
               res += ")";
               break;
            case MacroActionType.Remember:
               res += "remember to #" + pSlot;
               break;
            case MacroActionType.Restart:
               res += "restart application";
               break;
            case MacroActionType.GoTo:
               res += "activate #" + pSlot;
               break;

         }
         return res;
      }

      public string Serialize()
      {
         string res = "";
         if (pDevice != -1)
            res += "[" + pDevice + "]";
         res += string.Join("+", pNotModifiers.Select(i => "!" + i));
         if (pNotModifiers.Count > 0) res += "+";
         res += string.Join("+", pModifiers);
         if (pModifiers.Count > 0) res += "+";
         res += pKey;
         res += ",";

         switch (pMacroAction)
         {
            case MacroActionType.KeyStroke:
               res += "key " + pSimulateKey.ToString();
               break;
            case MacroActionType.Hibernate:
               res += "hibernate";
               break;
            case MacroActionType.Sleep:
               res += "sleep";
               break;
            case MacroActionType.WriteString:
               res += "string " + pChars;
               break;
            case MacroActionType.Run:
               res += "run " + pRun;
               break;
            case MacroActionType.AppMenu:
               res += "AppMenu\n";
               foreach (frmAppMenu.AppInfo appInfo in pAppList)
               {
                  res += appInfo.Serialize() + "\n";
               }
               res += "AppListEnd";
               break;
            case MacroActionType.GoTo:
               res += "setw " + pSlot;
               break;
            case MacroActionType.Remember:
               res += "getw " + pSlot;
               break;
            case MacroActionType.Restart:
               res += "Restart\n";
               break;
         }
         return res;
      }

      internal void Run(bool bKeyDown)
      {
         switch (pMacroAction)
         {
            case MacroActionType.KeyStroke:
               Win32.keybd_event((byte)m_SimulateKey, 0, bKeyDown ? 0 : Win32.KEYEVENTF_KEYUP, 0);
               break;
            case MacroActionType.Hibernate:
               if (!bKeyDown)
                  Application.SetSuspendState(PowerState.Hibernate, true, false);
               break;
            case MacroActionType.Sleep:
               if (!bKeyDown)
                  Application.SetSuspendState(PowerState.Suspend, true, false);
               break;
            case MacroActionType.WriteString:
               if (bKeyDown)
                  System.Windows.Forms.SendKeys.Send(m_chars);
               break;
            case MacroActionType.Run:
               if (!bKeyDown)
                  Process.Start(m_Run);
               break;
            case MacroActionType.AppMenu:
               {
                  if (!bKeyDown)
                     frmAppMenu.ShowMenu(pAppList);
               }
               break;
            case MacroActionType.Restart:
               Process.Start("RestartRemapper", "2000 \"" + Application.ExecutablePath + "\"");
               StatusIcon.Exit();
               break;
            case MacroActionType.Remember:
               m_windows[pSlot - 1] = ForeignWindow.ForegroundWindow;
               break;
            case MacroActionType.GoTo:
               if (!bKeyDown)
               {
                  if (m_windows[pSlot - 1] != null)
                  {
                     if (m_windows[pSlot - 1].WindowState == WindowShowStyle.ShowMinimized)
                        m_windows[pSlot - 1].Show(WindowShowStyle.Restore);
                     m_windows[pSlot - 1].TopMost = true;
                     m_windows[pSlot - 1].Focus();
                     ETTimer.ExecuteDelayed(200, delegate
                     {
                        m_windows[pSlot - 1].BringToFront();
                        m_windows[pSlot - 1].Activate();
                        m_windows[pSlot - 1].TopMost = false;
                     });
                  }
               }
               break;
         }

      }

      internal static Macro ReadMacro(StreamReader sr)
      {
         Macro macro = null;
         while (!sr.EndOfStream && macro == null)
         {
            string line = sr.ReadLine();

            string macroSpec = line.BeforeString("//").Trim();
            if (macroSpec.Contains(","))
            {
               macro = new Macro(macroSpec);
            }
         }

         if (macro != null && macro.pMacroAction == MacroActionType.AppMenu)
         {
            string line = null;
            macro.pAppList = new frmAppMenu.AppList();
            while (!sr.EndOfStream)
            {
               line = sr.ReadLine();
               line = line.BeforeString("//").Trim();
               if (line.Trim().ToLower() != "applistend")
               {
                  macro.pAppList.Add(new frmAppMenu.AppInfo(line));
               }
               else
                  break;
            }
         }
         return macro;
      }
   }
}
